//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
//     Copyright (c) Telligent Systems Corporation.  All rights reserved.
// </copyright> 
//------------------------------------------------------------------------------

using System;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using CommunityServer;
using CommunityServer.Components;


namespace CommunityServer.Controls {

    /// <summary>
    /// This Web control displays the textboxes needed to create a new user account.
    /// The user can enter their username and email, and a warning message is displayed if
    /// either the username or email message is already taken.  Once the user enters an available
    /// username and password, they will be sent a confirmation email with their password.
    /// </summary>
    [
        ParseChildren(true)
    ]
    public class CreateUser : SecureTemplatedWebControl {

		#region Member Variables
        protected CSContext csContext = CSContext.Current;
        protected AccountActivation activation;
		protected UserInvitation invitation = null;
        

        TextBox username;
        TextBox password;
        TextBox emailAddress;
        IButton createButton;
        RequiredFieldValidator usernameValidator;
        RequiredFieldValidator passwordValidator;
        RequiredFieldValidator emailValidator;
        RequiredFieldValidator password2Validator;
        RequiredFieldValidator email2Validator;
        RegularExpressionValidator emailRegExValidator;
        RequiredFieldValidator placeHolderValidator;
        RegularExpressionValidator usernameRegExValidator;
        CompareValidator compareEmail;
        CompareValidator comparePassword;
		CustomValidator usernameLengthValidator;
		RegularExpressionValidator passwordRegExValidator;
		bool passportAuthentication = false;
        CustomValidator passwordContentValidator;
        CheckBox acceptAgreement;
        RequiredCheckBoxValidator requiredAcceptAgreement;
        HyperLink acceptAgreementLink;
		TimezoneDropDownList timezone;
		Control contactCheckboxes;
		YesNoRadioButtonList allowSiteToContact;
		YesNoRadioButtonList allowSitePartnersToContact;

		#endregion

		#region Base class
        // *********************************************************************
        //  CreateUser
        //
        /// <summary>
        /// Constructor
        /// </summary>
        // ***********************************************************************/
        public CreateUser() : base() 
		{

            activation = csContext.SiteSettings.AccountActivation;

            // If the user is an Administrator or moderator, they can create accounts
            //
			User user = csContext.User;
            if (user.IsForumAdministrator || user.IsBlogAdministrator || user.IsGalleryAdministrator) 
			{
                activation = AccountActivation.Automatic;
            } 
			else 
			{
                if (!csContext.SiteSettings.AllowNewUserRegistration)
                    throw new CSException(CSExceptionType.UserAccountRegistrationDisabled);

				if (activation == AccountActivation.InvitationOnly)
				{
					// do we have an invitation key?
					if (Globals.IsNullorEmpty(csContext.Context.Request["InvitationKey"]))
						throw new CSException(CSExceptionType.UserAccountRequiresValidInvitation);

					// is the key a valid GUID?
					Guid invitationKey = Guid.Empty;
					try
					{
						invitationKey = new Guid(csContext.Context.Request["InvitationKey"]);
					}
					catch
					{
					}

					if (invitationKey == Guid.Empty)
						throw new CSException(CSExceptionType.UserAccountRequiresValidInvitation);

					// is the key a valid invitation?
					invitation = UserInvitations.GetUserInvitation(invitationKey);
					if (invitation == null)
						throw new CSException(CSExceptionType.UserAccountRequiresValidInvitation);
				}
			}

			
			//SkinName = "Skin-CreateNewAccount.ascx";


        }
		#endregion

        protected bool HasAgreement
        {
            get
            {
                return (acceptAgreementLink != null) && (acceptAgreement != null) && !Globals.IsNullorEmpty(csContext.SiteSettings.TermsOfServiceUrl);
            }
        }

		#region Init Skin
        // *********************************************************************
        //  Initializeskin
        //
        /// <summary>
        /// Initialize the control template and populate the control with values
        /// </summary>
        // ***********************************************************************/

		protected override void AttachChildControls()
		{
			if(Context.User.Identity.AuthenticationType.ToLower() == "passport")
				passportAuthentication = true;

			// What account activation mode are we in?
			//
			if (!passportAuthentication) 
			{
                switch (activation) 
                {

                    case AccountActivation.Automatic :
                    case AccountActivation.AdminApproval:
					case AccountActivation.InvitationOnly:
                        FindControl("AccountActivationAutomatic").Visible = true;
                        FindControl("AccountActivationAutomatic2").Visible = true;
                        break;
                    case AccountActivation.Email:
                        FindControl("AccountActivationAutomatic").Visible = false;
                        FindControl("AccountActivationAutomatic2").Visible = false;
                        break;

                }
			}

			//FindControl("EmailRow2").Visible = false;

            // Find the button on the user control and wire-it up to the CreateUser_Click event in this class
            createButton = FindButton("CreateAccount");
            createButton.Text = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_CreateAccount");
            createButton.Click += new System.EventHandler(CreateUser_Click);

            // Find the other controls
            username = (TextBox) FindControl("Username");
            password = (TextBox) FindControl("Password");
            
            emailAddress = (TextBox) FindControl("Email");
            usernameValidator = (RequiredFieldValidator) FindControl("usernameValidator");
            usernameValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_usernameValidator").Replace("'", @"\'");
            usernameRegExValidator = (RegularExpressionValidator) FindControl("usernameRegExValidator");
            usernameRegExValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_usernameRegExValidator").Replace("'", @"\'");
            passwordValidator = (RequiredFieldValidator) FindControl("passwordValidator");
            passwordValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_passwordValidator").Replace("'", @"\'");
            emailValidator = (RequiredFieldValidator) FindControl("emailValidator");
            emailValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_emailValidator").Replace("'", @"\'");
            emailRegExValidator = (RegularExpressionValidator) FindControl("emailRegExValidator");
            emailRegExValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_emailRegExValidator").Replace("'", @"\'");
			usernameRegExValidator.ValidationExpression = csContext.SiteSettings.UsernameRegex;            
            placeHolderValidator = (RequiredFieldValidator) FindControl("placeHolderValidator");
            comparePassword = (CompareValidator) FindControl("ComparePassword");
            comparePassword.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("ChangePassword_ReEnterNewPasswordRequired").Replace("'", @"\'");
            compareEmail = (CompareValidator) FindControl("CompareEmail");
            compareEmail.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_emailNoMatch").Replace("'", @"\'");
            password2Validator = (RequiredFieldValidator) FindControl("password2Validator");
            password2Validator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_passwordValidator").Replace("'", @"\'");
            email2Validator = (RequiredFieldValidator) FindControl("email2Validator");
            email2Validator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_emailValidator").Replace("'", @"\'");
			usernameLengthValidator = FindControl("usernameLengthValidator") as CustomValidator;
            usernameLengthValidator.ErrorMessage = string.Format(ResourceManager.GetString("CreateNewAccount_UsernameLimits"), csContext.SiteSettings.UsernameMinLength.ToString(), csContext.SiteSettings.UsernameMaxLength.ToString());
            passwordRegExValidator = FindControl("passwordRegExValidator") as RegularExpressionValidator;
			passwordRegExValidator.ValidationExpression = csContext.SiteSettings.PasswordRegex;
            passwordRegExValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_PasswordRegExValidator").Replace("'", @"\'");;
            timezone = (TimezoneDropDownList) FindControl("Timezone");
			if (timezone != null && timezone.Items.FindByValue(csContext.SiteSettings.TimezoneOffset.ToString()) != null)
				timezone.Items.FindByValue(csContext.SiteSettings.TimezoneOffset.ToString()).Selected = true;

			contactCheckboxes = FindControl("ContactCheckboxes");
			allowSiteToContact = FindControl("AllowSiteToContact") as YesNoRadioButtonList;
			if (allowSiteToContact != null)
				allowSiteToContact.SelectedValue = true;
			allowSitePartnersToContact = FindControl("AllowSitePartnersToContact") as YesNoRadioButtonList;
			if (allowSitePartnersToContact != null)
				allowSitePartnersToContact.SelectedValue = false;

			if (contactCheckboxes != null)
				contactCheckboxes.Visible = csContext.SiteSettings.ShowContactCheckboxes;
        	
			if (activation == AccountActivation.InvitationOnly)
			{
				if (emailAddress != null)
					emailAddress.Text = invitation.Email;

				TextBox email2 = FindControl("Email2") as TextBox;
				if (email2 != null)
					email2.Text = invitation.Email;
			}            

            passwordContentValidator = FindControl("passwordContentValidator") as CustomValidator;
            //passwordContentValidator.ErrorMessage = string.Format( ResourceManager.GetString("ChangePassword_InvalidLength"), ms.Membership.MinRequiredPasswordLength.ToString() );

            acceptAgreement = FindControl("AcceptAgreement") as CheckBox;
            requiredAcceptAgreement = FindControl("RequiredAcceptAgreement") as RequiredCheckBoxValidator;

            acceptAgreementLink = FindControl("AcceptAgreementLink") as HyperLink;

            

            if(HasAgreement)
            {
                if (requiredAcceptAgreement != null)
                    requiredAcceptAgreement.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_ServicesAgreementRequired").Replace("'", @"\'");

                acceptAgreementLink.Target = "_blank";
                acceptAgreementLink.NavigateUrl = csContext.SiteSettings.TermsOfServiceUrl;
                acceptAgreementLink.Text = ResourceManager.GetString("CreateNewAccount_ForumRulesDesc");
                acceptAgreementLink.Visible = true;
            }
            else
            {
                Control RulesSection = FindControl("RulesSection");
                if(RulesSection != null)
                    RulesSection.Visible = false;
            }

            UsersOnline.SetLocation("Registering",null);
        }
		#endregion

        #region Events

        private bool ValidateForm()
        {
            // Is valid?
            if (!Page.IsValid)
                return false;

            // Check for site services agreement
            if (!IsAgreementAccepted())
                return false;

            // Check the max length on the signature
            //
            if ((username.Text.Length > csContext.SiteSettings.UsernameMaxLength) || (username.Text.Length < csContext.SiteSettings.UsernameMinLength))
            {
                usernameLengthValidator.IsValid = false;
                return false;
            }

            string errorMessage = "";

            if (!passportAuthentication)
            {
                switch (activation)
                {

                    case AccountActivation.Automatic:
                    case AccountActivation.AdminApproval:
                    case AccountActivation.InvitationOnly:
                        if (!Users.PasswordIsMembershipCompliant(password.Text.Trim(), out errorMessage))
                        {
                            passwordContentValidator.IsValid = false;
                            passwordContentValidator.ErrorMessage = errorMessage;
                            return false;
                        }
                        break;
                }
            }

            // Reset the placeHolderValidator
            placeHolderValidator.IsValid = true;

            return true;
        }

        private User GetUserFromForm()
        {
            User user = new User();
            user.Username = username.Text;
            user.Email = emailAddress.Text;
            user.Password = password.Text.Trim();
            //user.PasswordFormat = CSContext.Current.SiteSettings.PasswordFormat;

            // Passport authentication
            if (passportAuthentication && Context.Items.Contains("PassportUID"))
            {
                user.AppUserToken = (string)Context.Items["PassportUID"];
                user.Password = user.AppUserToken;
            }

            // If we're using Email Account Activation, then we generate the password
            if (activation == AccountActivation.Email)
                user.Password = MemberRoleProfileProvider.Instance().Members.GeneratePassword(8, 2);

            // Does the user require approval?
            if (activation == AccountActivation.AdminApproval)
                user.AccountStatus = UserAccountStatus.ApprovalPending;
            else
                user.AccountStatus = UserAccountStatus.Approved;

            // Set the Anonymous flag to false
            user.IsAnonymous = false;

            return user;
        }

        protected CreateUserStatus CreateNewUser(User user)
        {
            // Attempt to create the user
            if (user == null || user.Username == "Anonymous")
            {
                return CreateUserStatus.DuplicateUsername;
            }
            else
            {

                try
                {
                    return Users.Create(user, true, csContext.User.IsAdministrator);
                }
                catch (CSException exception)
                {
                    return exception.CreateUserStatus;
                }
                catch (Exception ex)
                {
                    return MemberRoleProfileProvider.Instance().Members.GetCreateUserStatus(ex);
                }

            }
        }

        private void ErrorStatus(CreateUserStatus status)
        {
            switch (status)
            {

                // Username is disallowed
                case CreateUserStatus.DisallowedUsername:
                    placeHolderValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DisallowedUsername");
                    placeHolderValidator.IsValid = false;
                    break;

                // Username already exists!
                case CreateUserStatus.DuplicateUsername:
                    placeHolderValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DuplicateUsername");
                    placeHolderValidator.IsValid = false;
                    break;

                // Email already exists!
                case CreateUserStatus.DuplicateEmailAddress:
                    placeHolderValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DuplicateEmailAddress");
                    placeHolderValidator.IsValid = false;
                    break;

                case CreateUserStatus.InvalidEmail:
                    placeHolderValidator.ErrorMessage = ResourceManager.GetString("CreateNewAccount_CreateUserStatus_InvalidEmail");
                    placeHolderValidator.IsValid = false;
                    break;

                case CreateUserStatus.InvalidPassword:
                    placeHolderValidator.ErrorMessage = ResourceManager.GetString("CreateNewAccount_CreateUserStatus_InvalidPassword");
                    placeHolderValidator.IsValid = false;
                    break;

                case CreateUserStatus.InvalidQuestionAnswer:
                    placeHolderValidator.ErrorMessage = ResourceManager.GetString("CreateNewAccount_CreateUserStatus_QA");
                    placeHolderValidator.IsValid = false;
                    break;

                case CreateUserStatus.InvalidUserName:
                    placeHolderValidator.ErrorMessage = ResourceManager.GetString("CreateNewAccount_CreateUserStatus_InvalidUserName");
                    placeHolderValidator.IsValid = false;
                    break;

                // Unknown failure has occurred!
                case CreateUserStatus.UnknownFailure:
                    placeHolderValidator.ErrorMessage = CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_UnknownFailure");
                    placeHolderValidator.IsValid = false;
                    break;

            }
        }

        protected virtual void ProcessNewUser(User user)
        {
            SaveAdditionalProfileData(user);

            // the user was created, so the invitation, if it exists, has been accepted
            if (activation == AccountActivation.InvitationOnly && invitation != null)
                UserInvitations.Accept(invitation, user);

            switch (activation)
            {
                case AccountActivation.AdminApproval:
                    LeaveSecureConnection(Globals.GetSiteUrls().Message(CSExceptionType.UserAccountPending));
                    break;

                case AccountActivation.Email:
                    LeaveSecureConnection(Globals.GetSiteUrls().Message(CSExceptionType.UserAccountCreated));
                    break;

                case AccountActivation.Automatic:

                    // Send admins back to the manage user page.
                    if (csContext.User.IsAdministrator)
                        LeaveSecureConnection(Globals.GetSiteUrls().ControlPanelMembershipHome);

                    // Are we allowing login?
                    if (csContext.SiteSettings.AllowLogin)
                        if (!Page.Request.IsAuthenticated)
                            FormsAuthentication.SetAuthCookie(user.Username, true);
                    LeaveSecureConnection(Globals.GetSiteUrls().Message(CSExceptionType.UserAccountCreatedAuto));

                    break;

                case AccountActivation.InvitationOnly:

                    // Are we allowing login?
                    if (csContext.SiteSettings.AllowLogin)
                        if (!Page.Request.IsAuthenticated)
                            FormsAuthentication.SetAuthCookie(user.Username, true);

                    LeaveSecureConnection(Globals.GetSiteUrls().Message(CSExceptionType.UserAccountCreatedInvitationAccepted));

                    break;
            }
        }

        // *********************************************************************
        //  CreateUser_Click
        //
        /// <summary>
        /// This event handler fires when the submit button is clicked and the
        /// form posts back.  It is responsible for updating the user's info
        /// </summary>
        //
        // ********************************************************************/
		private void CreateUser_Click(Object sender, EventArgs e) 
		{
            if (!ValidateForm())
                return;



			// try to create the new user account
            User user = GetUserFromForm();

            CreateUserStatus status = CreateNewUser(user);

            if (status == CreateUserStatus.Created)
            {
                ProcessNewUser(user); 
            }
            else
            {
                ErrorStatus(status);
            }
		}

        #endregion

		#region public properties
        // *********************************************************************
        //  Redirect
        //
        /// <summary>
        /// Optionally don't perform redirect when creating a new user
        /// </summary>
        // ***********************************************************************/
		[
		System.ComponentModel.DefaultValue( true ),
		]
		public virtual Boolean Redirect {
			get {
				Object state = ViewState["Redirect"];
				if ( state != null ) {
					return (Boolean)state;
				}
				return true;
			}
			set {
				ViewState["Redirect"] = value;
			}
		}

		#endregion

        #region Helpers
        private bool IsAgreementAccepted () {
            // If services agreement is required
            //
            if (HasAgreement) {
                if (!acceptAgreement.Checked) { 

                    if(requiredAcceptAgreement != null)
                        requiredAcceptAgreement.IsValid = false;
                    
                    return false;
                }
            }

            // Agreement accept is not required or user has agreed ...
            //
            return true;
        }

		protected virtual void SaveAdditionalProfileData(User user)
		{
			if (timezone != null)
				user.Profile.Timezone = double.Parse(timezone.SelectedItem.Value);

			if (csContext.SiteSettings.ShowContactCheckboxes)
			{
				if (allowSiteToContact != null)
					user.AllowSiteToContact = allowSiteToContact.SelectedValue;

				if (allowSitePartnersToContact != null)
					user.AllowSitePartnersToContact = allowSitePartnersToContact.SelectedValue;
			}

			SetExtendedUserData(user);

			Users.UpdateUser(user);
		}

		private void SetExtendedUserData(User user)
		{
			foreach (string controlName in ExtendedUserData.Fields)
			{
				Control c  = FindControl(controlName);
				if (c != null)
				{
					string value = null;

					if (c is TextBox)
						value = ((TextBox)c).Text;
					else if (c is CheckBox)
						value = ((CheckBox)c).Checked.ToString();
					else if (c is ListControl)
						value = ((ListControl)c).SelectedValue;

					if (value != null)
						user.SetExtendedAttribute(c.ID, value);
				}
			}
		}
        #endregion
    }
}
